/*	game.c 
 *  Arizona State University
 *  CSE 325: Embedded Systems Programming
 *  Lab 6: Nunchuk Pacman 
 *  Georgii Tkachuk, Brentton Garber
 *   Emails: 	gtkachuk@asu.edu btgarber@asu.edu
 * 	Description: the game module specifies the rules of the game, the Ghosts moving patterns, the location of the barrier and the logic for player movements.
 * 
 */

#include "common.h"

// Declare our game variables
int player_dir = 4;
struct position {
	int col;
	int row;
	int ocol;
	int orow;
};
struct move {
	int dir;
	int restricted[4];
};
int ScreenMatrix[8][8];
int BarrierMaxtrix[8][8];
struct position Player;
struct position Ghosts[4];
int TotalGhosts = 1;
int program_mode = RUNNING;
int music_count = 0;

// Increase the number of ghosts on the board
void game_modGhost()
{
	TotalGhosts = ((TotalGhosts+1) % 4) + 1;
}

// Check for a valid move
int game_validMove(int row, int col)
{
	// Check if it will be out of bounds
	if(row < 0 || row > 7) return 0;
	if(col < 0 || col > 7) return 0;
	
	// Check if a barrier will be hit
	if(BarrierMaxtrix[row][col] == 1) return 0;
	
	return 1;
}

// Checks if the player hit a ghost
int game_GhostHit(int row, int col)
{
	int i;
	for(i=0; i < TotalGhosts; i++)
	{
		if(col == Ghosts[i].col && row == Ghosts[i].row)
			return 1;
	}
	return 0;
}

// Build the matrix that will be displayed to the screen
void game_buildGameScreen()
{
	int col, row;
	
	// For each row
	for(row=0; row < 8; row++)
	{
		// For each column
		for(col=0; col < 8; col++)
		{
			// Write the correct color to the dot
			if(BarrierMaxtrix[row][col] == 1)
				ScreenMatrix[row][col] = GREEN;
			else if(Player.row == row && Player.col == col && game_GhostHitPlayer() == 0)
				ScreenMatrix[row][col] = ORANGE;
			else if(game_GhostHit(row, col) == 1)
				ScreenMatrix[row][col] = RED;
			else
				ScreenMatrix[row][col] = BLACK;
		}
	}
		
	// Write the led matrix to the board
	ledmatrix_write(ScreenMatrix);	
}

// Called when the player changes direction with the nunchuk
void game_change_dir(int dir)
{
	if(dir == UP) player_dir = UP;
	else if(dir == DOWN) player_dir = DOWN;
	else if(dir == LEFT) player_dir = LEFT;
	else if(dir == RIGHT) player_dir = RIGHT;
}

// Called when the player has moved the joystick on the nunchuk
void game_movePlayer()
{
	int col, row;
	
	// Save old coordnates
	Player.ocol = Player.col;
	Player.orow = Player.row;
	
	row = Player.row;
	col = Player.col;
	
	// Update the players position
	if(player_dir == UP) row++;
	else if(player_dir == DOWN) row--;
	else if(player_dir == LEFT) col--;
	else if(player_dir == RIGHT) col++;
	
	// Verify for valid position on matrix
	if(game_validMove(row, col) == 1 && program_mode != LOST)
	{
		Player.col = col;
		Player.row = row;
	}
}

// Play a sound when the player moves
void player_move_sound()
{
	play_note(3, 0, 0);
}

// Play a sequence when the player dies
void player_dead_sound()
{
	int death_sound[] = {5,4,3,2,1,-1};
	play_note(death_sound[music_count], 0, 0);
	music_count++;
	MCF_PIT1_PCSR &= ~(1);
	if(music_count == 7)
	{
		music_count = 0;
		MCF_PIT0_PCSR &= ~(1);
		reset_pit_1();
		MCF_PIT0_PMR = 4999;
	}
}

// Updates the ghost positions on the screen
void game_moveGhosts_smart()
{
	int i, col, row, try, valid;
	struct move Dir;
		
	// Cycle through all the ghosts and try to update their positions
	for(i=0; i < TotalGhosts; i++)
	{
		Ghosts[i].ocol = Ghosts[i].col;
		Ghosts[i].orow = Ghosts[i].row;
		try = 0;
		do
		{
			// reset row and col to position of current ghost
			row = Ghosts[i].row;
			col = Ghosts[i].col;
			valid = 1;
			
			// try to follow the player
			if(Player.row > row && Dir.restricted[0] != UP && Dir.restricted[1] != UP && Dir.restricted[2] != UP)
				Dir.dir = UP;
			else if(Player.col > col && Dir.restricted[0] != RIGHT && Dir.restricted[1] != RIGHT && Dir.restricted[2] != RIGHT)
				Dir.dir = RIGHT;
			else if(Player.col < col && Dir.restricted[0] != LEFT && Dir.restricted[1] != LEFT && Dir.restricted[2] != LEFT)
				Dir.dir = LEFT;
			else if(Player.row < row && Dir.restricted[0] != DOWN && Dir.restricted[1] != DOWN && Dir.restricted[2] != DOWN)
				Dir.dir = DOWN;
			
			
			// Temporarily move the ghost in that direction
			if(Dir.dir == UP) row++;
			if(Dir.dir == DOWN) row--;
			if(Dir.dir == LEFT) col--;
			if(Dir.dir == RIGHT) col++;
			
			// check if that move was valid
			if(game_validMove(row, col) == 0) {
				Dir.restricted[try] = Dir.dir;
				valid = 0; 
			}
			if(game_GhostHit(row, col) == 1){
				Dir.restricted[try] = Dir.dir;
				valid = 0;
			}
			
			/*if(Player.row == row && Player.col == col) {
				Dir.restricted[try] = Dir.dir;
				valid = 0;
			}
			*/
			try++;
		}
		while(valid == 0 && try < 4 );
		
		// Update Ghost coordinates
		if(try < 4)
		{
			Ghosts[i].row = row;
			Ghosts[i].col = col;
		}
	}
}

// Randomly move the ghost on the matrix
void game_moveGhosts_rand()
{
	int i, col, row, try, dir, valid;
	
	// Cycle through all the ghosts and try to update their positions
	for(i=0; i < TotalGhosts; i++)
	{
		Ghosts[i].ocol = Ghosts[i].col;
		Ghosts[i].orow = Ghosts[i].row;
		try = 0;
		do
		{
			// reset row and col to position of current ghost
			row = Ghosts[i].row;
			col = Ghosts[i].col;
			valid = 1;
			
			// get a random direction
			dir = rand() % 4;
			
			// Temporarily move the ghost in that direction
			if(dir == UP) row++;
			if(dir == DOWN) row--;
			if(dir == LEFT) col--;
			if(dir == RIGHT) col++;
			
			// check if that move was valid
			if(game_validMove(row, col) == 0) valid = 0;
			if(game_GhostHit(row, col) == 1) valid = 0;
			//if(Player.row == row && Player.col == col) valid = 0;
			
			try++;
		}
		while(valid == 0 && try < 4 );
		
		// Update Ghost coordinates
		if(try < 4)
		{
			Ghosts[i].row = row;
			Ghosts[i].col = col;
		}
	}
}

// Check if a ghost hit a player
int game_GhostHitPlayer()
{
	int i;
	// cycle through each ghost
	for(i=0; i < TotalGhosts; i++)
	{
		// check for a valid hit
		if((Player.ocol == Ghosts[i].col && Player.orow == Ghosts[i].row) && (Player.col == Ghosts[i].ocol && Player.row == Ghosts[i].orow) )
			return 1;
		if(Player.col == Ghosts[i].col && Player.row == Ghosts[i].row)
			return 1;
	}
	return 0;
	
}

// Called by the pit timer to refreshed the game screen
void game_refresh()
{
	int prev_row, prev_col;
	// Move the ghosts on the screen
	static int count = 0;
	prev_row = Player.row;
	prev_col = Player.col;
	if(count<2)
	{
		game_moveGhosts_rand();
	}
	else 
		game_moveGhosts_smart();
	
	count++;
	if (count == 10)
		count = 0;
	game_movePlayer();
	
	// Update Screen
	game_buildGameScreen();
	
	if(game_GhostHitPlayer() == 1)
	{	
		play_note(-1, 0, 0);
		program_mode = LOST;
	}
	else if(Player.ocol != Player.col || Player.orow != Player.row)
	{
		player_move_sound();
	}

}

// Reset the game board. Called on game start and when reset button is pushed
void game_reset()
{
	int i, row, col, valid;
	
	// Set up the required barrier
	BarrierMaxtrix[2][2] = 1;
	BarrierMaxtrix[3][2] = 1;
	BarrierMaxtrix[4][2] = 1;
	BarrierMaxtrix[5][2] = 1;
	BarrierMaxtrix[5][3] = 1;
	
	
	// Set the players starting position
	player_dir = NONE;
	Player.col = 4;
	Player.row = 4;
	
	program_mode = RUNNING;
	music_count = 0;
	pit_init();
	// Spawn the ghosts
	for(i=0; i < 4; i++)
	{
		Ghosts[i].col = 0;
		Ghosts[i].row = 0;
	}
	for(i=0; i < TotalGhosts; i++)
	{
		// Find a valid starting position for the ghost
		do
		{
			valid = 1;
			row = rand() % 8;
			col = rand() % 8;
			
			// Make sure the ghost does not spawn on a barrier or out of bounds
			if(game_validMove(row, col) == 0) valid = 0;
			
			// make sure the ghost does not spawn too close to a player
			if(Player.col == col && Player.row == row) valid = 0;
			if(Player.col-1 == col && Player.row == row) valid = 0;
			if(Player.col+1 == col && Player.row == row) valid = 0;
			if(Player.col == col && Player.row-1 == row) valid = 0;
			if(Player.col == col && Player.row+1 == row) valid = 0;
		}
		while(valid == 0);
		
		// Set final ghost position
		Ghosts[i].col = col;
		Ghosts[i].row = row;
	}
	
}